package org.dce.screencapturer;

import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.awt.event.ContainerEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import javax.swing.JComponent;

import org.dce.screencapturer.actions.drawing.InsertObjectAction;
import org.dce.screencapturer.objects.DrawingObject;
import org.dce.screencapturer.util.UndoablePropertyChange;

public class Drawing 
	extends JComponent
	implements ClipboardOwner,
		PropertyChangeListener{

	private static final long serialVersionUID = 450801200566718353L;

	private InsertionHandler currentInsertion;

	private final SelectionHandler selectionHandler = new SelectionHandler(this);

	private UI ui;
	private BufferedImage image;
	
	@SuppressWarnings("unused")
	private final DragHandler dragHandler = new DragHandler(this);
	
	public SelectionHandler getSelectionHandler() {
		return selectionHandler;
	}

	public Drawing(){
		this(null);
		enableEvents(AWTEvent.MOUSE_EVENT_MASK);
		setFocusable(true);
	}

	protected void processMouseEvent(MouseEvent event){
		if (event.getID() == MouseEvent.MOUSE_CLICKED
			&& event.getButton() == MouseEvent.BUTTON1){
			requestFocus();
		}
		super.processMouseEvent(event);
	}
	
	public Drawing(BufferedImage mainImage){
		setImage(mainImage);
		enableEvents(AWTEvent.MOUSE_EVENT_MASK|AWTEvent.CONTAINER_EVENT_MASK);
		selectionHandler.addSelectionListener(new SelectionListener() {
			public void selectionChanged(SelectionEvent event) {
				repaint();
			}
		});
	}
	
	public UI getUI() {
		return ui;
	}

	public void setUI(UI ui) {
		this.ui = ui;
	}

	public BufferedImage toImage(){
		BufferedImage image = new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_INT_RGB);
		
		Graphics2D g2d = image.createGraphics();
		
		for (DrawingObject object:getDrawingObjects()){
			object.draw(g2d);
		}
		
		return image;
	}
	
	public BufferedImage getImage(){
		return image;
	}
	
	public void setImage(BufferedImage image){

		this.image = image;
		
		if (this.image != null){
			Dimension size = new Dimension(image.getWidth(),image.getHeight());
			setMinimumSize(size);
			setPreferredSize(size);
			setMaximumSize(size);
			setSize(size);
			revalidate();
		}
	}


	public List<DrawingObject> getDrawingObjects() {
		
		List<DrawingObject> objects = new ArrayList<DrawingObject>();
		
		for (Component c:getComponents()){
			if (c instanceof DrawingObject){
				objects.add((DrawingObject)c);
			}
		}
		return objects;
	}

	public void startInsertion(InsertObjectAction insertAction) {
		
		if (currentInsertion == null || currentInsertion.isFinished()){
			currentInsertion = new InsertionHandler(this,insertAction);
			currentInsertion.start();
		}
		
	}

	public void removeSelection() {
		for (DrawingObject object:new HashSet<DrawingObject>(selectionHandler.getSelection())){
			remove(object);
		}
		invalidate();
		repaint();
	}

	protected void paintComponent(Graphics g){
		g.drawImage(image,0,0,this);
		g.setColor(Color.BLACK);
		g.drawRect(0,0,image.getWidth()-1,image.getHeight()-1);
	}
	
	@Override
	public void lostOwnership(Clipboard clipboard, Transferable contents) {
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		
		if (ui != null 
			&& isUndoableProperty(evt.getPropertyName())){
			
			ui.getUndoManager().addEdit(
				new UndoablePropertyChange(
					evt.getSource(), 
					evt.getPropertyName(), 
					evt.getOldValue(), 
					evt.getNewValue()));
		}
	}

	private boolean isUndoableProperty(String propertyName) {
		return !"ancestor".equals(propertyName);
	}

	@Override
	protected void processContainerEvent(ContainerEvent e) {
		
		switch (e.getID()){
		case ContainerEvent.COMPONENT_ADDED:
			e.getChild().addPropertyChangeListener(this);
			break;
		case ContainerEvent.COMPONENT_REMOVED:
			e.getChild().removePropertyChangeListener(this);
			break;
		}
		super.processContainerEvent(e);
	}
}
